/*
 * boot.S
 * It is loaded at address 0x1000:0000 by the Boot2.
 * Modified by ps/2.
 */
 
 /* $Id: boot.S,v 1.2 2003/10/08 09:07:07 cnqin Exp $	*/


BOOTSEG = 0x07c0
SYSSEG	= 0x1000
SYSSIZE = 0x9000	# Max value may be 0x9000. 
SYSEND	= SYSSEG + SYSSIZE

CHANGE  = 0x200 + 0x23 + 2

.code16
.text
.global _start
_start:
	movw	$BOOTSEG, %ax
	movw	%ax, %ds
	movw	%ax, %es

	cli
	movw	%ax, %ss
	movw	$0x8000, %sp	# 0x8000 + 0x7c00 < 0x10000
	sti

# get extent memory size
#	movb	$0x88,%ah
#	int	$0x15
#	movw	%ax,(506)
	call	get_e820

	movw	$SYSSEG, %ax
	movw	%ax, %es
	
	
# Save the current cursor at address 0x7c00 + 510, 0x7c00 + 511.
	movb	$3, %ah
	xorb	%bh, %bh
	int	$0x10
	movw	%dx, (510)

# Set the SYSSEG
	movw	$SYSSEG, 	%ax
	movw	%ax,	%ds
	movw	%ax,	%es
# When we boot the system from the harddisk , we must 
# modify the offset in head.S before the head.S have moved itself
# from the 0x10000 to 0x100000.
	movb	$0x2,	CHANGE

# Now it's time to enter protected mode.
	cli
	lidt	idt_pdesc
	lgdt	gdt_pdesc

# Enable a20.
	call	kb_wait
	movb	$0xd1, %al	# command write.
	outb	%al, $0x64
	call	kb_wait
	movb	$0xdf, %al	# enable a20.
	outb	%al, $0x60
	call	kb_wait

# Assume all things is ok. Go on to initialize 8259.
	movb	$0x11, %al	# initialize sequence.
	outb	%al, $0x20	
	call	delay		
	outb	%al, $0xa0	
	call	delay
	movb	$0x20, %al	# start of hardware interrupt (0x20).
	outb	%al, $0x21
	call	delay
	movb	$0x28, %al	# start of hardware interrupt (0x28).
	outb	%al, $0xa1
	call	delay
	movb	$0x04, %al	# set master.
	outb	%al, $0x21
	call	delay
	movb	$0x02, %al	# set slave.
	outb	%al, $0xa1
	call	delay
	movb	$0x01, %al	# 8086 mode.
	outb	%al, $0x21
	call	delay
	outb	%al, $0xa1
	call	delay
	movb	$0xfb, %al	# mask all.
	outb	%al, $0x21
	call	delay
	movb	$0xff, %al
	outb	%al, $0xa1
	
# Actually enter protected mode.
	movw	$0x0001, %ax
	lmsw	%ax
	.byte	0x66, 0xea
code386:.long	0x10200		# the next sector is head.S and system
	.word	8

kb_wait:
	.word	0x00eb, 0x00eb	# Dealy
	inb	$0x64, %al	
	testb	$0x02, %al	# Keyboard buffer is full?
	jnz	kb_wait		# Yes, wait.
delay:
	ret

get_e820:
		pushfl
		pushal

		movl $-(1 << 20), E820Max # Max amount of high memory
		movl $(1 << 20), E820Mem  # End of detected high memory
.start_over:
		movw $E820Buf, %di
		xor %ax, %ax
		movw $10, %cx
		rep stosw			# Clear buffer
		xor %ebx, %ebx			# Start with first record
		jmp .do_e820		# Skip "at end" check first time!
.int_loop:	and %ebx, %ebx			# If we're back at beginning...
		jz .e820_done			# ... we're done
.do_e820:	movl $0x0000E820, %eax
		movl $0x534D4150, %edx		# "SMAP" backwards
		xor %ecx, %ecx
		movb $20, %cl			# ECX <- 20 (size of buffer)
		movw $E820Buf, %di
		int $0x15
		jnc .no_carry
		# If carry, ebx == 0 means error, ebx != 0 means we're done
		and %ebx, %ebx
		jnz .e820_done
		jmp no_e820
.no_carry:
		cmpl $0x534D4150, %eax
		jne no_e820
		cmpw $20, %cx
		jb no_e820

#
# Look for a memory block starting at <= 1 MB and continuing upward
#
		cmpl $0, E820Buf + 4
		ja .int_loop			# Start >= 4 GB?
		movl E820Buf, %eax
		cmpl $1, E820Buf + 16
		je .is_ram			# Is it memory?
		#
		# Non-memory range.  Remember this as a limit; some BIOSes get the length
		# of primary RAM incorrect!
		#
.not_ram:
		cmpl $(1<<20), %eax
		jb .int_loop			# Starts in lowmem region
		cmpl E820Max, %eax
		jae .int_loop			# Already above limit
		movl %eax, E820Max		# Set limit
		jmp .int_loop

.is_ram:
		cmpl E820Mem, %eax
		ja .int_loop			# Not contiguous with our starting point
		addl E820Buf + 8, %eax
		jc .overflow
		cmpl $0, E820Buf + 12
		je .nooverflow
.overflow:
		orl $-1, %eax
.nooverflow:
		cmpl E820Mem, %eax
		jbe .int_loop			# All is below our baseline
		movl %eax, E820Mem
		jmp .start_over			# Start over in case we find an adjacent range

.e820_done:
		movl E820Mem, %eax
		cmpl E820Max, %eax
		jna .not_limited
		movl E820Max, %eax
.not_limited:
		cmpl $(1 << 20), %eax
		ja got_highmem			# Did we actually find memory?
		# otherwise fall through

#
# INT 15:E820 failed.  Try INT 15:E801.
#
no_e820:
		movw $0xe801, %ax			# Query high memory (semi-recent)
		int $0x15
		jc no_e801
		cmpw $0x3c00, %ax
		ja no_e801			# > 3C00h something's wrong with this call
		jb e801_hole			# If memory hole we can only use low part

		movw %bx, %ax
		shll $16, %eax			# 64K chunks
		addl $(16 << 20), %eax		# Add first 16M
		jmp got_highmem

#
# INT 15:E801 failed.  Try INT 15:88.
#
no_e801:
		movb $0x88, %ah			# Query high memory (oldest)
		int $0x15
		cmpw $14*1024, %ax			# Don't trust memory >15M
		jna e801_hole
		movw  $14*1024, %ax
e801_hole:
		andl $0xffff, %eax
		shll $10, %eax # Convert from kilobytes
		addl $(1 << 20), %eax	# First megabyte
got_highmem:
		movl %eax, E820Mem
		popal
		popfl
		ret				# Done!

gdt:
	.word	0, 0, 0, 0

	.word	0x07ff		# limit == 8M (2048 * 4096)
	.word	0x0000		# base address = 0
	.word	0x9a00		# code read/exec
	.word	0x00c0		# unit = 4096, 386

	.word	0x07ff
	.word	0x0000
	.word	0x9200		# data read/write
	.word	0x00c0
	
idt_pdesc:
	.word	0
	.word	0, 0
	
gdt_pdesc:
	.word	8 * 3 - 1	# gdt limit = 24, 3 GDT entries
	.word	gdt , 0x1	# 0x10000 + gdt

E820Buf:
	.long 0, 0, 0, 0, 0	# INT 15:E820 data buffer
E820Max:
	.long 0			# Is E820 memory capped?
HighMemSize:
	.long 0			# End of memory pointer (bytes)
# to save memory size	
.org 506
E820Mem:
	.word 0,0
.org 510
	.word 0xaa55
